package com.aura.lyngen.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.aura.lyngen.annotation.AuthCheck;
import com.aura.lyngen.annotation.RateLimit;
import com.aura.lyngen.common.BaseResponse;
import com.aura.lyngen.common.DeleteRequest;
import com.aura.lyngen.common.ResultUtils;
import com.aura.lyngen.constant.AppConstant;
import com.aura.lyngen.constant.UserConstant;
import com.aura.lyngen.exception.BusinessException;
import com.aura.lyngen.exception.ErrorCode;
import com.aura.lyngen.exception.ThrowUtils;
import com.aura.lyngen.model.dto.app.*;
import com.aura.lyngen.model.entity.App;
import com.aura.lyngen.model.entity.User;
import com.aura.lyngen.model.enums.RateLimitType;
import com.aura.lyngen.model.vo.AppVO;
import com.aura.lyngen.service.AppService;
import com.aura.lyngen.service.ProjectDownloadService;
import com.aura.lyngen.service.UserService;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.io.File;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

import static com.aura.lyngen.constant.UserConstant.ADMIN_ROLE;

/**
 * 应用管理控制器
 * 负责应用的创建、更新、删除、查询等核心接口，区分普通用户接口与管理员接口
 *
 * @author ZJX
 * @since 2025-09-01 20:39:41
 */
@RestController
@RequestMapping("/app")
public class AppController {

    @Resource
    private AppService appService;

    @Resource
    private UserService userService;

    @Resource
    private ProjectDownloadService projectDownloadService;

    /**
     * 根据用户聊天输入的提示词 生成应用代码
     *
     * @param appId      应用ID
     * @param userPrompt 用户提示词
     * @param request    HTTP请求对象，用于获取当前登录用户
     * @return 返回一个Flux流，用于流式传输生成的代码
     */
    @GetMapping(value = "/chat/gen/code", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    @RateLimit(limitType = RateLimitType.USER, rate = 5, rateInterval = 60, message = "您操作太频繁了，请稍后再试")
    public Flux<ServerSentEvent<String>> chatToGenAppCode(@RequestParam Long appId,
                                                          @RequestParam("message") String userPrompt,
                                                          HttpServletRequest request) {
        // 参数校验
        ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_INVALID_FORMAT, "应用ID格式不正确");
        ThrowUtils.throwIf(StrUtil.isBlank(userPrompt), ErrorCode.AI_PROMPT_EMPTY, "用户消息不能为空");
        // 获取当前登录用户
        User loginUser = userService.getLoginUser(request);
        // 调用服务生成代码（流式）
        Flux<String> contentFlux = appService.chatToGenAppCode(appId, userPrompt, loginUser);
        // 转换为 ServerSentEvent 格式
        return contentFlux
                .map(chunk -> {
                    // 将内容包装成JSON对象
                    Map<String, String> wrapper = Map.of("v", chunk);
                    String jsonData = JSONUtil.toJsonStr(wrapper);
                    return ServerSentEvent.<String>builder()
                            .data(jsonData)
                            .build();
                })
                .concatWith(Mono.just(
                        // 发送结束事件
                        ServerSentEvent.<String>builder()
                                .event("done")
                                .data("")
                                .build()
                ));
    }


    /**
     * 普通用户 - 创建应用
     *
     * @param appAddRequest 应用创建请求参数（含初始化prompt等）
     * @param request       请求对象（用于获取当前登录用户）
     * @return 新增应用的ID
     */
    @PostMapping("/add")
    public BaseResponse<Long> createApp(@RequestBody AppAddRequest appAddRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(appAddRequest == null, ErrorCode.PARAMS_NULL, "应用创建请求不能为空");
        // 获取当前登录用户
        User loginUser = userService.getLoginUser(request);
        Long appId = appService.createApp(appAddRequest, loginUser);
        return ResultUtils.success(appId);
    }

    /**
     * 普通用户 - 更新应用（仅允许更新自己的应用名称）
     *
     * @param appUpdateRequest 应用更新请求参数（含应用ID、新名称）
     * @param request          请求对象（用于获取当前登录用户）
     * @return 更新结果（true/false）
     */
    @PostMapping("/update")
    public BaseResponse<Boolean> updateApp(@RequestBody AppUpdateRequest appUpdateRequest, HttpServletRequest request) {
        // 参数校验：请求对象或应用ID不能为空
        if (appUpdateRequest == null || appUpdateRequest.getId() == null) {
            throw new BusinessException(ErrorCode.PARAMS_NULL, "更新请求参数不能为空");
        }

        // 获取当前登录用户、查询原应用信息
        User loginUser = userService.getLoginUser(request);
        long appId = appUpdateRequest.getId();
        App oldApp = appService.getById(appId);
        ThrowUtils.throwIf(oldApp == null, ErrorCode.NOT_FOUND_ERROR); // 应用不存在

        // 权限校验：仅应用创建者可更新
        if (!oldApp.getUserId().equals(loginUser.getId())) {
            throw new BusinessException(ErrorCode.APP_PERMISSION_DENIED, "无权限更新该应用");
        }

        // 构造更新实体：仅更新ID、名称、编辑时间
        App app = new App();
        app.setId(appId);
        app.setAppName(appUpdateRequest.getAppName());
        app.setEditTime(LocalDateTime.now());

        // 执行更新并返回结果
        boolean result = appService.updateById(app);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        return ResultUtils.success(true);
    }


    /**
     * 下载应用代码
     *
     * @param appId    应用ID
     * @param request  请求
     * @param response 响应
     */
    @GetMapping("/download/{appId}")
    public void downloadAppCode(@PathVariable Long appId,
                                HttpServletRequest request,
                                HttpServletResponse response) {
        // 1. 基础校验
        ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_INVALID_FORMAT, "应用ID格式不正确");
        // 2. 查询应用信息
        App app = appService.getById(appId);
        ThrowUtils.throwIf(app == null, ErrorCode.APP_NOT_FOUND, "应用不存在");
        // 3. 权限校验：只有应用创建者可以下载代码
        User loginUser = userService.getLoginUser(request);
        if (!app.getUserId().equals(loginUser.getId())) {
            throw new BusinessException(ErrorCode.APP_PERMISSION_DENIED, "无权限下载该应用代码");
        }
        // 4. 构建应用代码目录路径（生成目录，非部署目录）
        String codeGenType = app.getCodeGenType();
        String sourceDirName = codeGenType + "_" + appId;
        String sourceDirPath = AppConstant.CODE_OUTPUT_ROOT_DIR + File.separator + sourceDirName;
        // 5. 检查代码目录是否存在
        File sourceDir = new File(sourceDirPath);
        ThrowUtils.throwIf(!sourceDir.exists() || !sourceDir.isDirectory(),
                ErrorCode.APP_CODE_NOT_GENERATED, "应用代码不存在，请先生成代码");
        // 6. 生成下载文件名（不建议添加中文内容）
        String downloadFileName = String.valueOf(appId);
        // 7. 调用通用下载服务
        projectDownloadService.downloadProjectAsZip(sourceDirPath, downloadFileName, response);
    }


    /**
     * 应用部署
     *
     * @param appDeployRequest 部署请求
     * @param request          请求
     * @return 部署 URL
     */
    @PostMapping("/deploy")
    public BaseResponse<String> deployApp(@RequestBody AppDeployRequest appDeployRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(appDeployRequest == null, ErrorCode.PARAMS_NULL, "部署请求不能为空");
        Long appId = appDeployRequest.getAppId();
        ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_INVALID_FORMAT, "应用 ID 格式不正确");
        // 获取当前登录用户
        User loginUser = userService.getLoginUser(request);
        // 调用服务部署应用
        String deployUrl = appService.deployApp(appId, loginUser);
        return ResultUtils.success(deployUrl);
    }

    /**
     * 普通用户 - 删除应用（仅允许删除自己的应用）
     *
     * @param deleteRequest 删除请求参数（含应用ID）
     * @param request       请求对象（用于获取当前登录用户）
     * @return 删除结果（true/false）
     */
    @PostMapping("/delete")
    public BaseResponse<Boolean> deleteApp(@RequestBody DeleteRequest deleteRequest, HttpServletRequest request) {
        // 参数校验：请求对象或应用ID不合法
        if (deleteRequest == null || deleteRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        // 获取当前登录用户、查询原应用信息
        User loginUser = userService.getLoginUser(request);
        long appId = deleteRequest.getId();
        App oldApp = appService.getById(appId);
        ThrowUtils.throwIf(oldApp == null, ErrorCode.NOT_FOUND_ERROR); // 应用不存在

        // 权限校验：仅应用创建者或管理员可删除
        if (!oldApp.getUserId().equals(loginUser.getId()) && !ADMIN_ROLE.equals(loginUser.getUserRole())) {
            throw new BusinessException(ErrorCode.APP_PERMISSION_DENIED, "无权限删除该应用");
        }

        // 执行删除并返回结果
        boolean result = appService.removeById(appId);
        return ResultUtils.success(result);
    }

    /**
     * 普通用户 - 根据ID获取应用详情（VO层封装，含用户信息）
     *
     * @param id 应用ID
     * @return 应用详情VO
     */
    @GetMapping("/get/vo")
    public BaseResponse<AppVO> getAppVOById(long id) {
        ThrowUtils.throwIf(id <= 0, ErrorCode.PARAMS_INVALID_FORMAT, "应用ID格式不正确");

        // 查询应用并封装VO
        App app = appService.getById(id);
        ThrowUtils.throwIf(app == null, ErrorCode.APP_NOT_FOUND, "应用不存在");
        return ResultUtils.success(appService.getAppVO(app));
    }

    /**
     * 普通用户 - 分页查询自己创建的应用列表
     *
     * @param appQueryRequest 分页查询参数（含页码、页大小等）
     * @param request         请求对象（用于获取当前登录用户）
     * @return 应用列表分页VO
     */
    @PostMapping("/my/list/page/vo")
    public BaseResponse<Page<AppVO>> listMyAppVOByPage(@RequestBody AppQueryRequest appQueryRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(appQueryRequest == null, ErrorCode.PARAMS_NULL, "查询请求不能为空");

        // 获取当前登录用户、限制页大小（最多20条）
        User loginUser = userService.getLoginUser(request);
        long pageSize = appQueryRequest.getPageSize();
        ThrowUtils.throwIf(pageSize > 20, ErrorCode.PARAMS_OUT_OF_RANGE, "每页最多查询 20 个应用");
        long pageNum = appQueryRequest.getPageNum();

        // 筛选条件：仅查询当前用户的应用
        appQueryRequest.setUserId(loginUser.getId());
        QueryWrapper queryWrapper = appService.getQueryWrapper(appQueryRequest);

        // 分页查询并封装VO
        Page<App> appPage = appService.page(Page.of(pageNum, pageSize), queryWrapper);
        Page<AppVO> appVOPage = new Page<>(pageNum, pageSize, appPage.getTotalRow());
        List<AppVO> appVOList = appService.getAppVOList(appPage.getRecords());
        appVOPage.setRecords(appVOList);

        return ResultUtils.success(appVOPage);
    }

    /**
     * 普通用户 - 分页查询精选应用列表（按优先级筛选）
     *
     * @param appQueryRequest 分页查询参数（含页码、页大小等）
     * @return 精选应用列表分页VO
     */
    @PostMapping("/good/list/page/vo")
    @Cacheable(
            value = "good_app_page",
            key = "T(com.aura.lyngen.utils.CacheKeyUtils).generateKey(#appQueryRequest)",
            condition = "#appQueryRequest.pageNum <= 10"
    )
    public BaseResponse<Page<AppVO>> listGoodAppVOByPage(@RequestBody AppQueryRequest appQueryRequest) {
        ThrowUtils.throwIf(appQueryRequest == null, ErrorCode.PARAMS_NULL, "查询请求不能为空");

        // 限制页大小（最多20条）
        long pageSize = appQueryRequest.getPageSize();
        ThrowUtils.throwIf(pageSize > 20, ErrorCode.PARAMS_OUT_OF_RANGE, "每页最多查询 20 个应用");
        long pageNum = appQueryRequest.getPageNum();

        // 筛选条件：仅查询精选应用（优先级=常量定义的精选优先级）
        appQueryRequest.setPriority(AppConstant.GOOD_APP_PRIORITY);
        QueryWrapper queryWrapper = appService.getQueryWrapper(appQueryRequest);

        // 分页查询并封装VO
        Page<App> appPage = appService.page(Page.of(pageNum, pageSize), queryWrapper);
        Page<AppVO> appVOPage = new Page<>(pageNum, pageSize, appPage.getTotalRow());
        List<AppVO> appVOList = appService.getAppVOList(appPage.getRecords());
        appVOPage.setRecords(appVOList);

        return ResultUtils.success(appVOPage);
    }


    // ============================== 管理员专属接口 ==============================
    // 注：所有管理员接口均通过 @AuthCheck(mustRole = 管理员角色) 校验权限

    /**
     * 管理员 - 删除应用（无创建者限制）
     *
     * @param deleteRequest 删除请求参数（含应用ID）
     * @return 删除结果（true/false）
     */
    @PostMapping("/admin/delete")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    public BaseResponse<Boolean> deleteAppByAdmin(@RequestBody DeleteRequest deleteRequest) {
        // 参数校验：请求对象或应用ID不合法
        if (deleteRequest == null || deleteRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        // 查询应用是否存在
        long appId = deleteRequest.getId();
        App oldApp = appService.getById(appId);
        ThrowUtils.throwIf(oldApp == null, ErrorCode.NOT_FOUND_ERROR);

        // 执行删除并返回结果
        boolean result = appService.removeById(appId);
        return ResultUtils.success(result);
    }

    /**
     * 管理员 - 更新应用（支持全字段更新，无创建者限制）
     *
     * @param appAdminUpdateRequest 管理员更新请求参数（含应用ID、待更新字段）
     * @return 更新结果（true/false）
     */
    @PostMapping("/admin/update")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    public BaseResponse<Boolean> updateAppByAdmin(@RequestBody AppAdminUpdateRequest appAdminUpdateRequest) {
        // 参数校验：请求对象或应用ID不能为空
        if (appAdminUpdateRequest == null || appAdminUpdateRequest.getId() == null) {
            throw new BusinessException(ErrorCode.PARAMS_NULL, "更新请求参数不能为空");
        }

        // 查询应用是否存在
        long appId = appAdminUpdateRequest.getId();
        App oldApp = appService.getById(appId);
        ThrowUtils.throwIf(oldApp == null, ErrorCode.NOT_FOUND_ERROR);

        // 构造更新实体：复制请求参数、设置编辑时间
        App app = new App();
        BeanUtil.copyProperties(appAdminUpdateRequest, app);
        app.setEditTime(LocalDateTime.now());

        // 执行更新并返回结果
        boolean result = appService.updateById(app);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        return ResultUtils.success(true);
    }

    /**
     * 管理员 - 根据ID获取应用详情（VO层封装）
     *
     * @param id 应用ID
     * @return 应用详情VO
     */
    @GetMapping("/admin/get/vo")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    public BaseResponse<AppVO> getAppVOByIdByAdmin(long id) {
        ThrowUtils.throwIf(id <= 0, ErrorCode.PARAMS_INVALID_FORMAT, "应用ID格式不正确");

        // 查询应用并封装VO
        App app = appService.getById(id);
        ThrowUtils.throwIf(app == null, ErrorCode.APP_NOT_FOUND, "应用不存在");
        return ResultUtils.success(appService.getAppVO(app));
    }

    /**
     * 管理员 - 分页查询所有应用列表（无创建者限制，支持多条件筛选）
     *
     * @param appQueryRequest 分页查询参数（含页码、页大小、筛选条件等）
     * @return 应用列表分页VO
     */
    @PostMapping("/admin/list/page/vo")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    public BaseResponse<Page<AppVO>> listAppVOByPageByAdmin(@RequestBody AppQueryRequest appQueryRequest) {
        ThrowUtils.throwIf(appQueryRequest == null, ErrorCode.PARAMS_NULL, "查询请求不能为空");

        // 获取分页参数
        long pageNum = appQueryRequest.getPageNum();
        long pageSize = appQueryRequest.getPageSize();

        // 构建查询条件、分页查询
        QueryWrapper queryWrapper = appService.getQueryWrapper(appQueryRequest);
        Page<App> appPage = appService.page(Page.of(pageNum, pageSize), queryWrapper);

        // 封装VO并返回
        Page<AppVO> appVOPage = new Page<>(pageNum, pageSize, appPage.getTotalRow());
        List<AppVO> appVOList = appService.getAppVOList(appPage.getRecords());
        appVOPage.setRecords(appVOList);

        return ResultUtils.success(appVOPage);
    }

    /**
     * 基础接口 - 根据主键删除应用（无权限校验，谨慎使用）
     *
     * @param id 应用主键ID
     * @return 删除结果（true/false）
     */
    @DeleteMapping("remove/{id}")
    public boolean remove(@PathVariable Long id) {
        return appService.removeById(id);
    }
}